package com.ruoyi.core.service.impl;

import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.ClientUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.core.resource.OperatorUserSystem;
import com.ruoyi.common.core.resource.OperatorUserSystemHolder;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.RedisCacheKeyHelper;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.core.domain.*;
import com.ruoyi.core.domain.custombean.*;
import com.ruoyi.core.mapper.*;
import com.ruoyi.core.service.ClientUserService;
import com.ruoyi.core.vo.CkConfigDTO;
import com.ruoyi.core.vo.ClientUserReChargeOperationVO;
import com.ruoyi.core.vo.ClientUserUpdateOperationVO;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.mapper.ClientUserMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.lang.Assert;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.util.*;
import java.util.concurrent.ExecutorService;

@Service
@Slf4j
public class ClientUserServiceImpl implements ClientUserService {

    @Autowired
    private ClientUserMapper clientUserMapper;

    @Autowired
    private ActivationCodeDetailMapper activationCodeDetailMapper;

    @Autowired
    private ClientUserProductRelationMapper clientUserProductRelationMapper;

    @Autowired
    private BatchActivationCodeSummaryMapper batchActivationCodeRecordMapper;
    @Autowired
    private ClientProductMapper clientProductMapper;
    @Autowired
    private ClientProductUserMapper clientProductUserMapper;

    @Autowired
    private ExecutorService concurrencyExecutorThreadPool;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private CkConfigMapper ckConfigMapper;


    @Override
    @Transactional
    public boolean update(ClientUserUpdateOperationVO clientUserUpdateOperationVO) {

        if (!(clientUserUpdateOperationVO.getPassword().length() >= 6)) {
            String message = String
                    .format("客户端用户 id:[%s],用户名 [%s] 必须长度至少为6位的字母或数字", clientUserUpdateOperationVO.getId(), clientUserUpdateOperationVO.getUsername());
            throw new CustomException(message);
        }
        ActivationCodeDetailQueryResult activationCodeDetailQueryResult = getActivationCodeDetailQueryResultAndAssertCode(
                clientUserUpdateOperationVO.getActivationCode());

        ClientUser queryUser = clientUserMapper.findById(clientUserUpdateOperationVO.getId());
        if (queryUser == null) {
            String message = String.format("客户端用户 id:[%s],用户名 [%s] 不存在", clientUserUpdateOperationVO.getId(), clientUserUpdateOperationVO.getUsername());
            throw new CustomException(message);
        }

        if (!queryUser.getUsername().equals(clientUserUpdateOperationVO.getUsername())) {
            String message = String.format("客户端用户名 [%s] 不允许修改", clientUserUpdateOperationVO.getUsername());
            throw new CustomException(message);
        }

        assertProductIdSetExist(clientUserUpdateOperationVO.getProductIds());

        //1、 用户信息更新
        ClientUser user = new ClientUser();
        user.setId(clientUserUpdateOperationVO.getId());
        user.setUsername(clientUserUpdateOperationVO.getUsername());
        String rawPassWord = clientUserUpdateOperationVO.getPassword();
        user.setRawPassword(rawPassWord);
        user.setPassword(SecurityUtils.encryptPassword(rawPassWord));
        user.setUpdateBy(SecurityUtils.getUsername());
        user.setRemark(clientUserUpdateOperationVO.getRemark());
        if (clientUserUpdateOperationVO.getStatus() != null) {
            user.setStatus(clientUserUpdateOperationVO.getStatus().getCode());
        }
        clientUserMapper.update(user);

        //  2、 新增 产品 权限
        Set<Long> requestProductIds = clientUserUpdateOperationVO.getProductIds();
        // 已存在的权限 //  筛选出删除掉的 权限id
        getExistProductIdByUserId(user.getUserId()).forEach(item -> {
            if (!requestProductIds.contains(item)) {
                clientUserProductRelationMapper.deleteByUserIdAndProductId(user.getUserId(), item);
            }
        });
        // 新增 权限
        List<ClientUserProductRelation> clientUserProductRelations = new ArrayList<>();
        Date expireTime = DateUtils.incrementBaseOnBaseDate(DateUtils.getNowDate(), 30, Calendar.DAY_OF_YEAR);
        requestProductIds.forEach(item -> {
            if (clientUserProductRelationMapper.findByUserIdAndProductId(user.getUserId(), item) == null) {
                ClientUserProductRelation clientUserProductRelation = new ClientUserProductRelation();
                clientUserProductRelation.setUserId(user.getUserId());
                clientUserProductRelation.setProductId(item);
                clientUserProductRelation.setExpireTime(expireTime);
                clientUserProductRelations.add(clientUserProductRelation);
            }

        });

        if (!clientUserProductRelations.isEmpty()) {
            clientUserProductRelationMapper.batchInsert(clientUserProductRelations);
        }

        // 3、 激活码 激活 产品权限时间
        applyActivationCodeOnClientUser(activationCodeDetailQueryResult, user);

        //4 如果codeId不为空,则写入cookie分配信息
        if(clientUserUpdateOperationVO.getCodeId() != null){
            updateCkConfig(new UpdateCkConfigParams(user.getUserId(),clientUserUpdateOperationVO.getCodeId()));
        }
        return true;
    }


    @Override
    public boolean recharge(ClientUserReChargeOperationVO clientUserReChargeOperationVO) {

        ClientUser queryUser = clientUserMapper.findByUsername(clientUserReChargeOperationVO.getUsername());
        if (queryUser == null) {
            String message = String.format("客户端用户 [%s] 不存在", clientUserReChargeOperationVO.getUsername());
            throw new CustomException(message);
        }

        if (!queryUser.getRawPassword().equals(clientUserReChargeOperationVO.getPassword())) {
            String message = String.format("客户端用户 [%s] 密码不正确", clientUserReChargeOperationVO.getUsername());
            throw new CustomException(message);
        }

        ActivationCodeDetailQueryResult activationCodeDetailQueryResult = getActivationCodeDetailQueryResultAndAssertCode(
                clientUserReChargeOperationVO.getActivationCode());


        applyActivationCodeOnClientUser(activationCodeDetailQueryResult, queryUser);

        return true;
    }

    private Set<Long> getExistProductIdByUserId(Long userId) {
        Set<Long> result = new HashSet<>();
        List<ClientUserProductRelation> clientUserProductRelations = clientUserProductRelationMapper.findByUserId(userId);
        if (clientUserProductRelations != null && !clientUserProductRelations.isEmpty()) {
            clientUserProductRelations.forEach(item -> result.add(item.getProductId()));
        }
        return result;
    }

    private ActivationCodeDetailQueryResult getActivationCodeDetailQueryResultAndAssertCode(String activationCode) {

        if (!StringUtils.hasText(activationCode)) {
            return null;
        } else {
//            String realCode = ActivationCodeGeneratorUtil.getRealCode(activationCode);
            String realCode = activationCode;
            ActivationCodeDetailQueryResult activationCodeDetailQueryResult = activationCodeDetailMapper.findByCode(realCode);
            if (activationCodeDetailQueryResult == null) {
                throw new CustomException("激活码不存在!");
            }
            if (ActivationCodeStatus.APPLIED == activationCodeDetailQueryResult.getStatus()) {
                throw new CustomException("激活码已过期!");
            }
            return activationCodeDetailQueryResult;
        }

    }

    private void setOnlineStatus(ClientUserListQueryResult result) {
        String userTokenUniqueKey = RedisCacheKeyHelper.getUserTokenUniqueKey(OperatorUserSystem.CLIENT_USER, result.getId());
        String token = redisCache.getCacheObject(userTokenUniqueKey);

        boolean onlineStatus = false;
        if (StringUtils.hasText(token)) {
            Claims claims = tokenService.parseToken(token);
            // 解析对应的权限以及用户信息
            String userKey = (String) claims.get(Constants.LOGIN_USER_ID);
            LoginUser user = redisCache.getCacheObject(userKey);
            onlineStatus = user != null;
        }

        result.setOnline(onlineStatus);
    }


    private void assertProductIdSetExist(Collection<Long> productIds) {
        for (Long productId : productIds) {
            if (clientProductMapper.selectClientProductById(productId) == null) {
                String message = String.format("批量新增用户失败,用户权限客户端产品不存在,产品id[%s]", productId);
                throw new CustomException(message);
            }

        }
    }

    @Override
    public List<ClientUserListQueryResult> findAll(ClientUserQuery clientUserQuery) {
        List<ClientUserListQueryResult> results = clientUserProductRelationMapper.findAllClientUser(clientUserQuery);
        if (results != null && !results.isEmpty()) {
            results.forEach(item -> {
                // 在线状态
                setOnlineStatus(item);
                // 在线使用的内置账号
                setOnlineProductUser(item);
                //设置分配的cookie
                setCookiesConfig(item);
            });
        }
        return results;
    }

    private void setCookiesConfig(ClientUserListQueryResult item){
        List<CkConfigDTO> ckConfigs = ckConfigMapper.findByUserId(item.getId());
        if(ckConfigs == null || ckConfigs.isEmpty()){
            item.setCkCode("随机");
        }else{
            StringBuilder code = new StringBuilder();
            for (CkConfigDTO ckConfig : ckConfigs) {
                if(ckConfig == null){
                    continue;
                }
                code.append(ckConfig.getCodeName()).append(",");
            }
            item.setCkCode(code.toString());
        }
    }


    @Override
    public List<ProductUserOnlineUsageResult> getOnlineProductUserByClientUserId(Long clientUserId) {
        List<ProductUserOnlineUsageResult> results = new ArrayList<>();

        String keyPattern = Constants.PRODUCT_USAGE_KEY + "*" + RedisCacheKeyHelper.SPLIT + clientUserId;

        Collection<String> keys = redisCache.keys(keyPattern);
        if (keys != null && !keys.isEmpty()) {
            keys.forEach(key -> {
                ProductUserOnlineUsageResult result = new ProductUserOnlineUsageResult();
                String[] elements = StringUtils.trimArrayElements(StringUtils.delimitedListToStringArray(key, RedisCacheKeyHelper.SPLIT));

                //Long productId, Long productUserId, Long clientUserId
                Long productId = Long.parseLong(elements[1]);
                Long productUserId = Long.parseLong(elements[2]);
                Long clientUserIdInCache = Long.parseLong(elements[3]);

                ClientProduct clientProduct = clientProductMapper.selectClientProductById(productId);
                ClientProductUser clientProductUser = clientProductUserMapper.selectClientProductUserById(productUserId);

                result.setClientUserId(clientUserIdInCache);
                result.setProductId(productId);
                result.setProductName(clientProduct.getName());

                result.setProductUserId(productUserId);
                result.setProductUsername(clientProductUser.getUsername());
                results.add(result);
            });
        }


        return results;
    }

    private void setOnlineProductUser(ClientUserListQueryResult item) {
        String keyPattern = Constants.PRODUCT_USAGE_KEY + "*" + RedisCacheKeyHelper.SPLIT + item.getId();
        Collection<String> keys = redisCache.keys(keyPattern);
        int size = 0;
        if (keys != null && !keys.isEmpty()) {
            size = keys.size();
        }
        item.setProductUsers(size);
    }

    @Override
    public List<ClientUserListQueryResult> findByIdSet(Long[] ids) {
        List<ClientUserListQueryResult> results = clientUserProductRelationMapper.findClientUserByIdSet(ids);
        if (results != null && !results.isEmpty()) {
            results.forEach(this::setOnlineStatus);
        }
        return results;
    }

    @Override
    public int startOrStop(Long id, UserStatus userStatus) {
        if (UserStatus.DELETED == userStatus) {
            return 0;
        }
        ClientUser user = new ClientUser();
        user.setId(id);
        user.setStatus(userStatus.getCode());
        user.setUpdateBy(SecurityUtils.getUsername());
        return clientUserMapper.update(user);
    }


    @Override
    public List<ClientUserProductUsageRightQueryResult> findClientProductByClientUserId(Long clientUserId) {
        return clientUserProductRelationMapper.findClientProductByClientUserId(clientUserId);
    }

    @Override
    public int deleteByIdSet(Long[] ids) {
        return clientUserMapper.deleteByIdSet(ids);
    }


    private void applyActivationCodeOnClientUser(ActivationCodeDetailQueryResult activationCodeDetailQueryResult, ClientUser user) {
        if (activationCodeDetailQueryResult == null) {
            return;
        }
        Assert.notNull(user);
        concurrencyExecutorThreadPool.submit(() -> {
            final Long segment = activationCodeDetailQueryResult.getSegment();
            SegmentTimeUnit timeUnit = activationCodeDetailQueryResult.getTimeUnit();
            List<ClientUserProductRelation> clientUserProductRelations = new ArrayList<>();
            List<ClientProduct> clientProducts = batchActivationCodeRecordMapper.findClientProductByBatchId(activationCodeDetailQueryResult.getBatchId());
            clientProducts.forEach(clientProduct -> {
                Long productId = clientProduct.getId();
                ClientUserProductRelation clientUserProductRelation = clientUserProductRelationMapper.findByUserIdAndProductId(user.getUserId(), productId);

                if (clientUserProductRelation != null) {
                    Date date = DateUtils.getNowDate();
                    if(clientUserProductRelation.getExpireTime().compareTo(new Date()) > 0){
                        //如果还没过期 则从此基础上增加
                        date = clientUserProductRelation.getExpireTime();
                    }
                    clientUserProductRelation
                            .setExpireTime(DateUtils.incrementBaseOnBaseDate(date, Math.toIntExact(segment),
                                    timeUnit.getNumber()));
                    clientUserProductRelationMapper.update(clientUserProductRelation);
                } else {
                    ClientUserProductRelation relation = new ClientUserProductRelation();
                    relation.setUserId(user.getUserId());
                    relation.setProductId(productId);
                    relation.setExpireTime(DateUtils.incrementBaseOnBaseDate(DateUtils.getNowDate(), Math.toIntExact(segment), timeUnit.getNumber()));
                    clientUserProductRelations.add(relation);
                }
            });

            if (!clientUserProductRelations.isEmpty()) {
                clientUserProductRelationMapper.batchInsert(clientUserProductRelations);
            }

            activationCodeDetailMapper.updateStatusById(activationCodeDetailQueryResult.getId(), ActivationCodeStatus.APPLIED);
        });

    }

    @Override
    public int removeUser(Long id) {
        Assert.notNull(id,"请选择用户");
        int isOk = clientUserMapper.deleteByIdSet(new Long[]{id});
        if(isOk > 0){
            ckConfigMapper.deleteByUserId(id);
        }
        return isOk;
    }

    @Override
    public boolean updateCkConfig(UpdateCkConfigParams updateCkConfigParams) {

        if(updateCkConfigParams.getCodeId() == -1L){
            //说明是要删除
            ckConfigMapper.deleteByUserId(updateCkConfigParams.getUserId());
            return true;
        }

        //获取ckId对应的ProductId 如果存在一样的则替换 否则添加
        ClientProductUser clientProductUser = clientProductUserMapper.selectClientProductUserById(updateCkConfigParams.getCodeId());
        ClientProduct clientProduct = clientProductMapper.selectClientProductById(clientProductUser.getProductId());
        Assert.notNull(clientProduct,"异常的产品类型");
        CkConfig ckConfig = ckConfigMapper.findByUserIdAndProductId(updateCkConfigParams.getUserId(),clientProductUser.getProductId());
        if(ckConfig != null){
            //说明已经存在，需要替换
            ckConfigMapper.deleteByPrimaryKey(ckConfig.getId());
        }
        ckConfig = new CkConfig();
        ckConfig.setUserId(updateCkConfigParams.getUserId());
        ckConfig.setProductId(clientProductUser.getProductId());
        ckConfig.setCkCode(clientProductUser.getCode());
        ckConfig.setCkId(clientProductUser.getId());
        ckConfig.setProductName(clientProduct.getName());
        ckConfig.setCreateTime(DateUtils.format(new Date()));
        ckConfigMapper.insertSelective(ckConfig);
        return true;
    }
}
